package com.sas.service;

import com.sas.entity.Employee;
import com.sas.entity.Room;
import com.sas.entity.Customer;
import com.sas.entity.Schedule;
import com.sas.repository.EmployeeRepository;
import com.sas.repository.RoomRepository;
import com.sas.repository.CustomerRepository;
import com.sas.repository.ScheduleRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 排班算法服务类
 * 实现基于员工优先级的自动排班算法
 * 优先级：全职 > 兼职 > 临时工
 */
@Service
@Transactional
public class SchedulingAlgorithmService {

    @Autowired
    private EmployeeRepository employeeRepository;

    @Autowired
    private RoomRepository roomRepository;

    @Autowired
    private CustomerRepository customerRepository;

    @Autowired
    private ScheduleRepository scheduleRepository;

    /**
     * 自动排班主方法
     * @param startDate 开始日期
     * @param endDate 结束日期
     * @param employeeTypes 参与排班的员工类型
     * @param startTime 工作开始时间
     * @param endTime 工作结束时间
     * @return 生成的排班列表
     */
    public List<Schedule> autoSchedule(LocalDate startDate, LocalDate endDate,
                                       List<Integer> employeeTypes,
                                       LocalTime startTime, LocalTime endTime) {
        
        // 1. 获取可用员工，按优先级排序
        List<Employee> availableEmployees = getAvailableEmployeesByPriority(employeeTypes);
        
        // 2. 获取可用房间
        List<Room> availableRooms = getAvailableRooms();
        
        // 3. 获取需要服务的客户
        List<Customer> activeCustomers = getActiveCustomers();
        
        // 4. 生成排班计划
        List<Schedule> schedules = new ArrayList<>();
        
        // 遍历日期范围
        LocalDate currentDate = startDate;
        while (!currentDate.isAfter(endDate)) {
            // 跳过周末（可配置）
            if (!isWorkingDay(currentDate)) {
                currentDate = currentDate.plusDays(1);
                continue;
            }
            
            // 为当天生成排班
            List<Schedule> dailySchedules = generateDailySchedule(
                currentDate, availableEmployees, availableRooms, 
                activeCustomers, startTime, endTime
            );
            
            schedules.addAll(dailySchedules);
            currentDate = currentDate.plusDays(1);
        }
        
        // 5. 保存排班记录
        return scheduleRepository.saveAll(schedules);
    }

    /**
     * 获取按优先级排序的可用员工
     * 优先级：全职 > 兼职 > 临时工
     */
    private List<Employee> getAvailableEmployeesByPriority(List<Integer> employeeTypes) {
        List<Employee> employees = employeeRepository.findByEmployeeTypeInAndStatus(
            employeeTypes, 1
        );
        
        // 按优先级和工作能力排序
        return employees.stream()
            .sorted((e1, e2) -> {
                // 首先按员工类型优先级排序
                int priorityCompare = getEmployeePriority(e1.getEmployeeType())
                                    - getEmployeePriority(e2.getEmployeeType());
                if (priorityCompare != 0) {
                    return priorityCompare;
                }

                // 然后按工作能力排序（降序）
                // 当前实体未定义工作能力，保持原顺序
                return 0;
            })
            .collect(Collectors.toList());
    }

    /**
     * 获取员工类型优先级数值
     * 数值越小优先级越高
     */
    private int getEmployeePriority(Integer employeeType) {
        if (employeeType == null) return 4;
        switch (employeeType) {
            case 1: // 全职
                return 1;
            case 2: // 兼职
                return 2;
            case 3: // 临时工
                return 3;
            default:
                return 4;
        }
    }

    /**
     * 获取可用房间
     */
    private List<Room> getAvailableRooms() {
        return roomRepository.findAvailableRooms();
    }

    /**
     * 获取活跃客户
     */
    private List<Customer> getActiveCustomers() {
        return customerRepository.findByStatus(1);
    }

    /**
     * 判断是否为工作日
     */
    private boolean isWorkingDay(LocalDate date) {
        DayOfWeek dayOfWeek = date.getDayOfWeek();
        // 周一到周日都是工作日（养老院特殊性）
        return true;
    }

    /**
     * 解析房间内的客户ID列表（支持 JSON 数组或逗号分隔字符串）
     */
    private Set<Long> parseCustomerIds(String customerIds) {
        if (customerIds == null) {
            return Collections.emptySet();
        }
        String cleaned = customerIds.trim();
        if (cleaned.isEmpty()) {
            return Collections.emptySet();
        }
        // 去除常见的包裹字符：[ ], 空格, 引号
        cleaned = cleaned.replaceAll("[\\[\\]\\s\"']", "");
        if (cleaned.isEmpty()) {
            return Collections.emptySet();
        }
        Set<Long> result = new HashSet<>();
        for (String part : cleaned.split(",")) {
            if (part == null) continue;
            String p = part.trim();
            if (p.isEmpty()) continue;
            try {
                result.add(Long.parseLong(p));
            } catch (NumberFormatException ignored) {
                // 跳过无法解析的项
            }
        }
        return result;
    }

    /**
     * 生成单日排班计划
     */
    private List<Schedule> generateDailySchedule(LocalDate date, 
                                               List<Employee> employees,
                                               List<Room> rooms,
                                               List<Customer> customers,
                                               LocalTime startTime, 
                                               LocalTime endTime) {
        
        List<Schedule> dailySchedules = new ArrayList<>();
        
        // 获取当天已有的排班，避免冲突
        List<Schedule> existingSchedules = scheduleRepository.findByScheduleDate(date);
        
        // 创建员工工作时间跟踪器
        Map<Long, List<TimeSlot>> employeeWorkSlots = new HashMap<>();
        for (Employee employee : employees) {
            employeeWorkSlots.put(employee.getId(), new ArrayList<>());
        }
        
        // 初始化已有排班的时间占用
        for (Schedule existing : existingSchedules) {
            Long employeeId = existing.getEmployeeId();
            if (employeeWorkSlots.containsKey(employeeId)) {
                employeeWorkSlots.get(employeeId).add(
                    new TimeSlot(existing.getStartTime(), existing.getEndTime())
                );
            }
        }
        
        // 为每个房间分配员工和客户
        for (Room room : rooms) {
            // 获取该房间的客户
            Set<Long> roomCustomerIds = parseCustomerIds(room.getCustomerIds());
            List<Customer> roomCustomers = customers.stream()
                .filter(c -> roomCustomerIds.contains(c.getId()))
                .collect(Collectors.toList());
            
            if (roomCustomers.isEmpty()) {
                continue;
            }
            
            // 根据客户护理等级确定所需工作时长
            int totalCareHours = calculateRequiredCareHours(roomCustomers);
            
            // 分配员工到该房间
            List<Schedule> roomSchedules = assignEmployeesToRoom(
                date, room, roomCustomers, employees, employeeWorkSlots,
                startTime, endTime, totalCareHours
            );
            
            dailySchedules.addAll(roomSchedules);
        }
        
        return dailySchedules;
    }

    /**
     * 计算客户所需的护理小时数
     */
    private int calculateRequiredCareHours(List<Customer> customers) {
        int totalHours = 0;
        for (Customer customer : customers) {
            Integer level = customer.getCareLevel();
            if (level == null) {
                totalHours += 4;
            } else if (level == 3) {
                totalHours += 8; // 重度护理
            } else if (level == 2) {
                totalHours += 6; // 中度护理
            } else {
                totalHours += 4; // 轻度护理
            }
        }
        return Math.min(totalHours, 12); // 单个房间最多12小时工作时间
    }

    /**
     * 为房间分配员工
     */
    private List<Schedule> assignEmployeesToRoom(LocalDate date, Room room,
                                               List<Customer> customers,
                                               List<Employee> employees,
                                               Map<Long, List<TimeSlot>> employeeWorkSlots,
                                               LocalTime startTime, LocalTime endTime,
                                               int requiredHours) {
        
        List<Schedule> roomSchedules = new ArrayList<>();
        int remainingHours = requiredHours;
        LocalTime currentTime = startTime;
        
        // 按优先级为房间分配员工
        for (Employee employee : employees) {
            if (remainingHours <= 0) {
                break;
            }
            
            // 检查员工当天工作时间是否超限
            if (getTotalWorkHours(employeeWorkSlots.get(employee.getId())) >= getMaxWorkHours(employee)) {
                continue;
            }
            
            // 计算该员工可工作的时间段
            LocalTime employeeEndTime = calculateEmployeeEndTime(
                currentTime, remainingHours, employee, employeeWorkSlots.get(employee.getId())
            );
            
            if (employeeEndTime.isAfter(currentTime) && !employeeEndTime.isAfter(endTime)) {
                // 创建排班记录
                Schedule schedule = createSchedule(
                    employee, room, customers.get(0), // 主要客户
                    date, currentTime, employeeEndTime
                );
                
                roomSchedules.add(schedule);
                
                // 更新员工工作时间
                employeeWorkSlots.get(employee.getId()).add(
                    new TimeSlot(currentTime, employeeEndTime)
                );
                
                // 更新剩余时间和当前时间
                int workedHours = (int) java.time.Duration.between(currentTime, employeeEndTime).toHours();
                remainingHours -= workedHours;
                currentTime = employeeEndTime;
            }
        }
        
        return roomSchedules;
    }

    /**
     * 计算员工的最大工作小时数
     */
    private int getMaxWorkHours(Employee employee) {
        Integer type = employee.getEmployeeType();
        if (type == null) return 4;
        switch (type) {
            case 1:
                return 8; // 全职员工最多8小时
            case 2:
                return 6; // 兼职员工最多6小时
            case 3:
                return 4; // 临时工最多4小时
            default:
                return 4;
        }
    }

    /**
     * 计算员工当天总工作小时数
     */
    private int getTotalWorkHours(List<TimeSlot> workSlots) {
        return workSlots.stream()
            .mapToInt(slot -> (int) java.time.Duration.between(slot.getStartTime(), slot.getEndTime()).toHours())
            .sum();
    }

    /**
     * 计算员工结束工作时间
     */
    private LocalTime calculateEmployeeEndTime(LocalTime startTime, int remainingHours,
                                             Employee employee, List<TimeSlot> existingSlots) {
        
        int maxHours = getMaxWorkHours(employee);
        int currentWorkHours = getTotalWorkHours(existingSlots);
        int availableHours = Math.min(maxHours - currentWorkHours, remainingHours);
        
        return startTime.plusHours(Math.max(1, availableHours)); // 至少工作1小时
    }

    /**
     * 创建排班记录
     */
    private Schedule createSchedule(Employee employee, Room room, Customer customer,
                                  LocalDate date, LocalTime startTime, LocalTime endTime) {
        
        Schedule schedule = new Schedule();
        schedule.setEmployeeId(employee.getId());
        schedule.setRoomId(room.getId());
        schedule.setCustomerId(customer.getId());
        schedule.setScheduleDate(date);
        schedule.setStartTime(startTime);
        schedule.setEndTime(endTime);
        schedule.setStatus(1); // 1-待确认
        java.time.Duration d = java.time.Duration.between(startTime, endTime);
        schedule.setWorkHours(java.math.BigDecimal.valueOf(Math.max(1, d.toHours())));
        schedule.setCreateTime(LocalDateTime.now());
        schedule.setUpdateTime(LocalDateTime.now());
        schedule.setRemarks("自动排班生成");
        
        return schedule;
    }

    /**
     * 优化排班算法 - 负载均衡
     * 确保员工工作负载相对均衡
     */
    public void optimizeScheduleBalance(LocalDate startDate, LocalDate endDate) {
        List<Schedule> schedules = scheduleRepository.findByDateRange(startDate, endDate);
        
        // 按员工分组统计工作时间
        Map<Long, Integer> employeeWorkHours = schedules.stream()
            .collect(Collectors.groupingBy(
                schedule -> schedule.getEmployeeId(),
                Collectors.summingInt(schedule -> 
                    (int) java.time.Duration.between(schedule.getStartTime(), schedule.getEndTime()).toHours()
                )
            ));
        
        // 找出工作时间差异较大的员工进行调整
        OptionalInt maxHours = employeeWorkHours.values().stream().mapToInt(Integer::intValue).max();
        OptionalInt minHours = employeeWorkHours.values().stream().mapToInt(Integer::intValue).min();
        
        if (maxHours.isPresent() && minHours.isPresent() && maxHours.getAsInt() - minHours.getAsInt() > 10) {
            // 执行负载均衡调整
            balanceWorkload(schedules, employeeWorkHours);
        }
    }

    /**
     * 执行工作负载均衡
     */
    private void balanceWorkload(List<Schedule> schedules, Map<Long, Integer> employeeWorkHours) {
        // 找出工作时间最多和最少的员工
        Long maxWorkEmployeeId = employeeWorkHours.entrySet().stream()
            .max(Map.Entry.comparingByValue())
            .map(Map.Entry::getKey)
            .orElse(null);
        
        Long minWorkEmployeeId = employeeWorkHours.entrySet().stream()
            .min(Map.Entry.comparingByValue())
            .map(Map.Entry::getKey)
            .orElse(null);
        
        if (maxWorkEmployeeId != null && minWorkEmployeeId != null) {
            // 尝试将部分排班从工作时间多的员工转移到工作时间少的员工
            List<Schedule> maxEmployeeSchedules = schedules.stream()
                .filter(s -> Objects.equals(s.getEmployeeId(), maxWorkEmployeeId))
                .sorted(Comparator.comparing(Schedule::getScheduleDate).reversed())
                .collect(Collectors.toList());
            
            Employee minWorkEmployee = employeeRepository.findById(minWorkEmployeeId).orElse(null);
            
            if (minWorkEmployee != null && !maxEmployeeSchedules.isEmpty()) {
                // 转移最近的一个排班
                Schedule scheduleToTransfer = maxEmployeeSchedules.get(0);
                
                // 检查是否可以转移（时间冲突等）
                if (canTransferSchedule(scheduleToTransfer, minWorkEmployee)) {
                    scheduleToTransfer.setEmployeeId(minWorkEmployee.getId());
                    scheduleToTransfer.setRemarks(scheduleToTransfer.getRemarks() + " - 负载均衡调整");
                    scheduleRepository.save(scheduleToTransfer);
                }
            }
        }
    }

    /**
     * 检查是否可以转移排班
     */
    private boolean canTransferSchedule(Schedule schedule, Employee newEmployee) {
        // 检查新员工在该时间段是否有冲突
        List<Schedule> conflictSchedules = scheduleRepository.findByEmployeeIdAndDateAndTimeOverlap(
            newEmployee.getId(), schedule.getScheduleDate(), schedule.getStartTime(), schedule.getEndTime()
        );
        
        return conflictSchedules.isEmpty();
    }

    /**
     * 时间段内部类
     */
    private static class TimeSlot {
        private LocalTime startTime;
        private LocalTime endTime;
        
        public TimeSlot(LocalTime startTime, LocalTime endTime) {
            this.startTime = startTime;
            this.endTime = endTime;
        }
        
        public LocalTime getStartTime() {
            return startTime;
        }
        
        public LocalTime getEndTime() {
            return endTime;
        }
    }

    /**
     * 获取排班统计信息
     */
    public Map<String, Object> getSchedulingStatistics(LocalDate startDate, LocalDate endDate) {
        List<Schedule> schedules = scheduleRepository.findByDateRange(startDate, endDate);
        
        Map<String, Object> statistics = new HashMap<>();
        
        // 总排班数
        statistics.put("totalSchedules", schedules.size());
        
        // 参与员工数
        long uniqueEmployees = schedules.stream()
            .map(Schedule::getEmployeeId)
            .distinct()
            .count();
        statistics.put("uniqueEmployees", uniqueEmployees);
        
        // 总工作小时数
        int totalHours = schedules.stream()
            .mapToInt(s -> (int) java.time.Duration.between(s.getStartTime(), s.getEndTime()).toHours())
            .sum();
        statistics.put("totalHours", totalHours);
        
        // 按员工类型统计
        // 员工类型统计（通过员工ID关联查询）
        Set<Long> employeeIds = schedules.stream().map(Schedule::getEmployeeId).collect(Collectors.toSet());
        Map<Long, Employee> employeesById = employeeRepository.findAllById(employeeIds).stream()
            .collect(Collectors.toMap(Employee::getId, e -> e));
        Map<Integer, Long> typeStatistics = schedules.stream()
            .map(s -> {
                Employee e = employeesById.get(s.getEmployeeId());
                return e != null ? e.getEmployeeType() : null;
            })
            .filter(Objects::nonNull)
            .collect(Collectors.groupingBy(t -> t, Collectors.counting()));
        statistics.put("typeStatistics", typeStatistics);
        
        // 按状态统计
        Map<Integer, Long> statusStatistics = schedules.stream()
            .collect(Collectors.groupingBy(
                Schedule::getStatus,
                Collectors.counting()
            ));
        statistics.put("statusStatistics", statusStatistics);
        
        return statistics;
    }
}